// main 以广度优先算法搜索指定目录下的文件或子目录
package main

import (
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"time"
)

const APP_ID = "findbsf"

var root string
var max int
var count int
var filter []Filter

type Filter interface {
	match(path string, info os.FileInfo) bool //过滤器
}
type TypeFilter struct {
	t string
}

func (f *TypeFilter) match(path string, info os.FileInfo) bool {
	var match bool
	switch f.t {
	case "":
		match = true
	case "d":
		match = info != nil && info.IsDir()
	case "f":
		match = info != nil && !info.IsDir()
	default:
		match = false
	}
	return match
}

type WildcardFilter struct {
	wildcard string
}

func (f *WildcardFilter) match(path string, info os.FileInfo) bool {
	if f.wildcard == "" {
		return true
	}
	path = filepath.Base(path)
	m, _ := filepath.Match(f.wildcard, path)
	return m
}
func NewWildcardFilter(wildcard string) (*WildcardFilter, error) {
	wildcard = strings.TrimSpace(wildcard)
	if _, e := filepath.Match(wildcard, "abc"); e != nil {
		return nil, fmt.Errorf("通配符【%s】错误:%s", wildcard, e.Error())
	}
	f := &WildcardFilter{wildcard: wildcard}
	return f, nil
}

type RegFilter struct {
	reg   *regexp.Regexp
	ptype string
}

func (f *RegFilter) match(path string, info os.FileInfo) bool {
	m := f.reg.MatchString(path)
	return m
}
func NewRegFilter(reg string) (*RegFilter, error) {
	p, e := regexp.Compile(reg)
	if e != nil {
		return nil, fmt.Errorf("正则表达式【%s】错误:%s", reg, e.Error())
	}
	return &RegFilter{reg: p}, nil
}

//将路径拆分为多个部分，忽略前后分隔符
func SplitPath(path string) []string {
	path = filepath.Clean(path)
	sp := string(filepath.Separator)
	path = strings.Trim(path, sp)
	p := strings.Split(path, sp)
	return p
}

var skip bool

//按指定通配符查找此目录下的子目录
func main() {
	fmt.Println("findbsf v0.4.2 liuyl162@chinaunicom.cn 以广度优先算法搜索指定目录下的文件或子目录")
	wd, _ := os.Getwd()
	flag.StringVar(&root, "d", wd, "指定搜索根目录")
	flag.IntVar(&max, "m", 1, "最大搜索深度")
	var f_wc, f_reg, f_type string
	flag.StringVar(&f_wc, "w", "20[12][09][0-9][0-9][0-9][0-9]", "文件名通配符，按路径或文件名过滤")
	flag.StringVar(&f_reg, "r", "", "正则表达式，按相对路径过滤")
	flag.StringVar(&f_type, "t", "", "限定类型 d-目录，f-文件")
	flag.BoolVar(&skip, "skip", true, "如果目录匹配成功，不再检测其内部")
	flag.Parse()

	var err error
	root, err = filepath.Abs(root)
	if err != nil {
		fmt.Println("无效目录:", root, err.Error())
		os.Exit(1)
	}
	if f_type != "" {
		filter = append(filter, &TypeFilter{t: f_type})
	}
	if f_reg != "" {
		if f, e := NewRegFilter(f_reg); e != nil {
			fmt.Println(e.Error())
			os.Exit(1)
		} else {
			filter = append(filter, f)
		}
	}
	if f_wc != "" {
		if f, err := NewWildcardFilter(f_wc); err != nil {
			fmt.Println(err.Error())
			os.Exit(1)
		} else {
			filter = append(filter, f)
		}
	}

	t0 := time.Now()
	FindDir(root)
	t1 := time.Now().Sub(t0)
	fmt.Printf("result %d, in %s, cast %v\n", count, root, t1)
}

func FindDir(root string) {
	err := filepath.Walk(root, match_path)
	if err != nil {
		fmt.Fprintln(os.Stdout, "Err:", err.Error())
	}
}

//正则表达式匹配相对路径，限制层级
func match_path(path string, info os.FileInfo, err error) error {
	if info == nil || err != nil {
		fmt.Println("Err:", path, err.Error())
		return nil
	}

	if path == root { //根目录跳过
		return nil
	}

	//过滤
	var match bool = true
	for _, f := range filter {
		if !f.match(path, info) {
			match = false
			break
		}
	}

	if match {
		fmt.Fprintln(os.Stdout, path)
		count++
	}

	if info != nil && info.IsDir() {
		if err != nil {
			return filepath.SkipDir
		}
		if match {
			return filepath.SkipDir
		}
		rel, _ := filepath.Rel(root, path)
		depth := len(SplitPath(rel))
		if depth >= max { //跳过目录
			return filepath.SkipDir
		}
	}

	// fmt.Println("Debug:", depth, is_dir, match_type, match_path, rel)
	return nil
}
